package nobnak.study.gl;

import java.util.LinkedList;

public class LineCliping {
	public static final float EPSILON = 1e-3f;
	public static final int BIT_LEFT = 1;
	public static final int BIT_RIGHT = BIT_LEFT << 1;
	public static final int BIT_BOTTOM = BIT_RIGHT << 1;
	public static final int BIT_TOP = BIT_BOTTOM << 1;
	
	private float left;
	private float right;
	private float bottom;
	private float top;
	
	// Constructors
	public LineCliping(float left, float right, float bottom, float top) {
		this.left = left;
		this.right = right;
		this.bottom = bottom;
		this.top = top;
	}
	
	// Methods
	public int computeOutcode(float x, float y) {
		int result = 0;
		result |= (EPSILON < (left - x)) ? BIT_LEFT : 0;
		result |= (EPSILON < (x - right)) ? BIT_RIGHT : 0;
		result |= (EPSILON < (bottom - y)) ? BIT_BOTTOM : 0;
		result |= (EPSILON < (y - top)) ? BIT_TOP : 0;
		return result;
	}
	public Clip[] clipStrip(float[] strip) {
		LinkedList<Clip> result = new LinkedList<LineCliping.Clip>();
		int limit = strip.length - 2;
		for (int i = 0; i < limit; i+= 2) {
			float x0 = strip[i], y0 = strip[i+1];
			float x1 = strip[i+2], y1 = strip[i+3];
			Clip c = clipLine(x0, y0, x1, y1, i);
			if (c != null)
				result.add(c);
		}
		return result.toArray(new Clip[result.size()]);
	}
	public Clip clipLine(float x0, float y0, float x1, float y1, int index) {
		int outcode0 = computeOutcode(x0, y0);
		int outcode1 = computeOutcode(x1, y1);
		
		while (true) {
			if ((outcode0 | outcode1) == 0)
				return new Clip(x0, y0, x1, y1, index);
			if ((outcode0 & outcode1) != 0)
				return null;
			
			int outcode = (outcode0 != 0) ? outcode0 : outcode1;
			float x, y;
			if ((outcode & BIT_LEFT) != 0) {
				x = left;
				y = y0 + (y1 - y0) * (left - x0) / (x1 - x0);
			} else if ((outcode & BIT_RIGHT) != 0) {
				x = right;
				y = y0 + (y1 - y0) * (right - x0) / (x1 - x0);
			} else if ((outcode & BIT_BOTTOM) != 0) {
				x = x0 + (x1 - x0) * (bottom - y0) / (y1 - y0);
				y = bottom;
			} else {
				x = x0 + (x1 - x0) * (top - y0) / (y1 - y0);
				y = top;
			}
			
			if (outcode == outcode0) {
				x0 = x;
				y0 = y;
				outcode0 = computeOutcode(x0, y0);
			} else {
				x1 = x;
				y1 = y;
				outcode1 = computeOutcode(x1, y1);
			}
		}
	}
	
	// Interfaces
	public class Clip {
		public final float x0, y0, x1, y1;
		public final int index;
		
		public Clip(float x0, float y0, float x1, float y1, int index){
			this.x0 = x0;
			this.y0 = y0;
			this.x1 = x1;
			this.y1 = y1;
			this.index = index;
		}
	}
}
